package minesweeper;

import interfaces.Minesweepable;

import java.util.Random;

public class Minesweeper implements Minesweepable {

	protected final int NUMBER_OF_MINES; // medium - 30 %, player can choose 10 % -50 % range
	protected int[][] minesweeperData;
	private OppenedMinesweeper oppenedSquares;
	private FlaggedMinesweeper flagsSquares;
	
	public static Minesweeper createMinesweeperData(int rows, int columns) {
		if(rows > 0 && columns > 0) {
			Minesweeper minesweeper = new Minesweeper(rows, columns);
			minesweeper.createField();
			return minesweeper;
		}
		else
			return null;
	}
	
	@Override
	public boolean changeFlagSquareState(int row, int col) {
		if(this.flagsSquares.isFlaggedSquare(row, col) && this.flagsSquares.flagsLeft() < 0) {
			//no more mines left
			return false;
		}
		
		this.flagsSquares.changeFlagSquareState(row, col);
		return true;
	}

	@Override
	public boolean isFlagged(int row, int col) {
		return this.flagsSquares.isFlaggedSquare(row, col);
	}
	
	@Override
	public boolean isGameWon() {
		int oppenedSquares = 0;

		if(flagsSquares.isAllFlagsCorectlyPlaced())
			return true;
		
		for(int i = 0; i < this.minesweeperData.length; i++)
			for(int j = 0; j < this.minesweeperData[i].length; j++) {
				if(isOpened(i, j))
					oppenedSquares++;
			}
		
		return oppenedSquares + NUMBER_OF_MINES == this.minesweeperData.length * this.minesweeperData[0].length;
	}

	@Override
	public int getNumberOfFlagsLeft() {
		return this.flagsSquares.flagsLeft();
	}
	
	@Override
	public void openSquare(int row, int column) {
		if(isValidExistaintSquare(row, column))
			this.oppenedSquares.openSquareWithRippleEffect(row, column);
	}

	@Override
	public int getSquare(int row, int column) {
		if(isValidExistaintSquare(row, column))
			return this.minesweeperData[row][column];
		return -2; //invalid square
	}
	
	@Override
	public boolean isOpened(int row, int column) {
		if(isValidExistaintSquare(row, column))
			return oppenedSquares.isOppenedSquare(row, column);
		return false;
	}
	
	protected Minesweeper(int rows, int cols) {
		NUMBER_OF_MINES = rows * cols * 30 / 100; // 30% of all squares are going to be mines
		this.minesweeperData = new int[rows][cols];
		
		this.oppenedSquares = OppenedMinesweeper.createOppenedMinesweeper(this,
				this.minesweeperData.length,
				this.minesweeperData[0].length);
		
		this.flagsSquares = FlaggedMinesweeper.createFlaggedMinesweeper(this,
				this.minesweeperData.length,
				this.minesweeperData[0].length,
				this.NUMBER_OF_MINES);
	}
	
	private void createField() {
		putMines();
		putSavingBlocks();
	}

	protected void putSavingBlocks() {
		for(int i = 0; i < minesweeperData.length; i++) {
			for(int j = 0; j < minesweeperData[i].length; j++) {
				//if it a save-stepping field
				if(minesweeperData[i][j] != -1) {
					minesweeperData[i][j] = getSurroundedMines(i, j);
				}
			}
		}
	}

	private void putMines() {
		Random rnd = new Random();
		int row = -1;
		int col = -1;
		for(int i = 1; i <= NUMBER_OF_MINES; i++) {
			row = rnd.nextInt(minesweeperData.length); // get random row
			col = rnd.nextInt(minesweeperData[row].length); // get random column
			
			//check if there already is a mine
			if(!putMine(row, col)) i--; // try again
		}
	}

	protected boolean putMine(int onRow, int onCol) {
		if(this.minesweeperData[onRow][onCol] != -1) {
			this.minesweeperData[onRow][onCol] = -1; // put mine
			return true;
		}
		
		return false;
	}
	
	protected int getSurroundedMines(int row, int col) {
		int minesAroundRowCol = 0;
		
		for(int i = -1; i < 2; i++)
			for(int j = -1; j < 2; j++) 
				try {
					if(this.minesweeperData[row + i][col + j] == -1) {
						minesAroundRowCol++;
					}
				}
				catch(IndexOutOfBoundsException e) {
					//we MUST do nothing -> skip borders/eat exception! (exception ate)
				}

		return minesAroundRowCol;
	}
	
	protected boolean isValidExistaintSquare(int row, int column) {
		return row >= 0 && column >= 0 && row < minesweeperData.length && column < minesweeperData[row].length;
	}
}